// SPDX-License-Identifier: GPL-2.0

#define pr_fmt(fmt) "DIM Hash: " fmt

#include <linux/kernel.h>
#include <crypto/hash.h>
#include <linux/err.h>
#include <uapi/linux/hash_info.h>
#include <linux/vmalloc.h>

#include "dim_hash.h"

/* dim global hash index */
static enum dim_hash_type g_hash_index = DIM_HASH_SHA256;
/* dim hash algorithm tfm table */
static struct crypto_shash *hash_tfm[DIM_HASH_LAST] = { NULL };
/* dim hash name table */
static const char *const dim_hash_name[DIM_HASH_LAST] = {
	[DIM_HASH_SHA256] = "sha256",
};

enum dim_hash_type dim_hash_algo_sys_2_dim(int algo)
{
	switch (algo) {
	case HASH_ALGO_SHA256:
		return DIM_HASH_SHA256;
	default:
		return DIM_HASH_LAST;
	}
}

void dim_hash_algo_uninit(void)
{
	int i;

	for (i = 0; i < DIM_HASH_LAST; i++) {
		if (hash_tfm[i] != NULL) {
			crypto_free_shash(hash_tfm[i]);
			hash_tfm[i] = NULL;
		}
	}
}

int dim_hash_algo_init(void)
{
	int i;

	for (i = 0; i < DIM_HASH_LAST; i++) {
		if (hash_tfm[i] == NULL)
			hash_tfm[i] = crypto_alloc_shash(dim_hash_name[i], 0, 0);

		if (IS_ERR(hash_tfm[i])) {
			hash_tfm[i] = NULL;
			if (i == dim_hash_get_cur_index()) {
				pr_err("algorithm init tfm failed: %ld\n",
					PTR_ERR(hash_tfm[i]));
				return -ENOMEM;
			}
		}
	}

	return 0;
}

enum dim_hash_type dim_hash_get_index_by_name(const char *name)
{
	int i;

	if (name == NULL)
		return DIM_HASH_LAST;

	for (i = 0; i < DIM_HASH_LAST; i++) {
		if (strcmp(dim_hash_name[i], name) == 0)
			return i;
	}

	return DIM_HASH_LAST;
}

enum dim_hash_type dim_hash_get_cur_index(void)
{
	return g_hash_index;
}

void dim_hash_set_cur_index(const char *algo)
{
	g_hash_index = dim_hash_get_index_by_name(algo);
	if (g_hash_index >= DIM_HASH_LAST)
		g_hash_index = DIM_HASH_SHA256;
}

int dim_hash_get_digest_len(enum dim_hash_type hash_index)
{
	if (hash_index == DIM_HASH_SHA256)
		return 32;

	return 0;
}

const char *dim_hash_get_name_by_index(enum dim_hash_type hash_index)
{
	if (hash_index >= DIM_HASH_LAST)
		return "";

	return dim_hash_name[hash_index];
}

struct crypto_shash *dim_hash_get_tfm(enum dim_hash_type hash_index)
{
	if (hash_index >= DIM_HASH_LAST)
		return NULL;

	return hash_tfm[hash_index];
}
